import * as THREE from "./three.js/build/three.module.js";
import { AmmoLib } from "./A3Class.js";

import { 
  ISLAND_SLEEPING,
  DISABLE_DEACTIVATION,
  DISABLE_SIMULATION,

  addAxesHelper,
  createMaterial,
  createRigidBody, 
  getMeshShape, 
  setActorUserPointer, 
  isNull,

  disposeMesh,
  toAmmoType,
} from "./A3Common.js";
import settings from "./A3Settins.js";

// const defaultMass = 1.0;

// 演员类
class A3Actor {

  name = "actor";
  type = "actor";

  parent = undefined;
  
  axes = undefined;
  showAxes = true;

  textureLoader = new THREE.TextureLoader();

  // 运动控制
  targetLinearVelocity = new THREE.Vector3();
  targetAngularVelocity = new THREE.Vector3();
  linearMotor = false;
  angularMotor = false;

  enable=false;

  friction = 0.5;

  constructor() {

    // 默认演员

    this.threeObject = undefined;    // 视觉模型
    this.physicsShape = undefined;   // 碰撞形状
    this.physicsBody = undefined;    // 碰撞刚体

    this.needsUpdate = false;
    this.position = new THREE.Vector3( 0, 0, 0 );
    this.quaternion = new THREE.Quaternion( 0, 0, 0, 1 );
    this.inertia = new THREE.Vector3(0, 0, 0);
    this.mass = 0;

    this.worldTransform = new AmmoLib.btTransform();

  }

  init( mass, position, quaternion, inertia ) {

    const physicsShape = this.physicsShape;
    const threeObject = this.threeObject;

    mass = isNull(mass) ? this.mass : mass ;
    position = isNull(position) ? this.position : position;
    quaternion = isNull(quaternion) ? this.quaternion : quaternion;
    inertia = isNull(inertia) ? this.inertia : inertia;

    if( threeObject && physicsShape ) {

      const body = createRigidBody( physicsShape, mass, position, quaternion, inertia );
      body.setFriction( this.friction );

      threeObject.position.copy( position );
      threeObject.quaternion.copy( quaternion );
      
      this.physicsBody = body;

    }

    // setUserPointer( threeObject, body );
    setActorUserPointer( this );

    this.enableShadow();
    this.enalbeAxes(true);
    this.enableMatertial();

    return this;

  }

  setFromMesh( mesh, mass, position, quaternion, inertia, scale=new THREE.Vector3(1, 1, 1), isTriangle=false ) {

    const shape = getMeshShape( mesh, scale, isTriangle );

    if( isNull(position) ) {
      position = mesh.position.clone();
    }
    else {
      mesh.position.copy( position );
    }

    if( isNull(quaternion) ) {
      quaternion = mesh.quaternion.clone();
    }
    else {
      mesh.quaternion.copy( quaternion );
    }

    if( isNull( mass ) ) {
      mass = 0;
    }

    if( isNull( inertia ) ) {
      inertia = this.inertia;
    }

    this.threeObject = mesh;
    this.physicsShape = shape;

    this.position.copy( position );
    this.quaternion.copy( quaternion );
    this.mass = mass;
    this.inertia.copy(this.inertia);

    this.init();

  }

  setMaterial( material ) {

    if( isNull( material ) ) {
      material = createMaterial();
    }
    else {
      
      if( !isNull( this.material ) ) {
        this.material.dispose();
      }

      this.material = material;

    }

    if( !isNull(this.threeObject) ) {
      this.threeObject.material = this.material;
    }

  }

  enableMatertial( enable=true ) {
    
    if( isNull(this.threeObject) ) return;

    if( enable ) {
      const material = isNull( this.material ) ? createMaterial() : this.material ;
      this.material = material;
      this.threeObject.material = material;
    }
    else {

      // if( this.threeObject.material ) {
      //   this.threeObject.material.dispose();
      // }

      this.threeObject.material = null;

    }

  }

  setMass( mass ) {

    if( isNull( mass ) ) {

      mass = 0;

    }

    if( !isNull( this.physicsBody ) ) {
      if( !isNull( this.world ) ) {
        this.world.removeRigidBody( this.physicsBody );
      }
    }

    if( !isNull(this.physicsBody) ) {

      const body = createRigidBody( this.physicsShape, mass, this.position, this.quaternion, this.inertia );
    
      setActorUserPointer( this );

      this.physicsBody = body;

      if( !isNull( this.world ) ) {
        this.world.addRigidBody( this.physicsBody );
      }
    }

    this.mass = mass;
  }

  setTexture( texture ) {

    // console.log( texture );

    if( isNull( this.threeObject ) ) return;

    if( isNull( this.threeObject.material ) ) {
      this.enableMatertial();
    }

    this.threeObject.material.map = texture;
    this.threeObject.material.needsUpdate = true;

  }

  loadTexture( textureFile ) {

    this.textureLoader.load( textureFile, ( texture ) => {
  
      texture.wrapS = THREE.RepeatWrapping;
      texture.wrapT = THREE.RepeatWrapping;
      texture.repeat.set( 1, 1 );
      
      this.setTexture( texture );

    } );

  }

  enableSimulation( enable = true ){
    if ( this.physicsBody ) {
      this.physicsBody.setActivationState( enable ? DISABLE_DEACTIVATION :  DISABLE_SIMULATION );
    }
  }

  addToWord( parent ) {

    if( !isNull( this.threeObject ) ) {
      parent.scene.add( this.threeObject );
    }

    if( !isNull( this.physicsBody ) ){
      parent.world.addRigidBody( this.physicsBody );
    }

    this.scene = parent.scene;
    this.world = parent.world;
    this.parent = parent;

  }

  setAngularVelocity( av ) {
    if( !isNull( this.physicsBody ) ) {
      this.physicsBody.setAngularVelocity( toAmmoType( av ));
    }
  }

  enableLinearMotor( enable, lv ) {
    this.targetLinearVelocity.copy(lv);
    this.linearMotor = enable;
  }

  enableAnglurMotor( enable, av ) {
    this.targetLinearVelocity.copy(av);
    this.angularMotor = enable;
  }

  // setTargetLinearVelocity( lv ) {
  //   this.targetLinearVelocity.copy(lv);
  //   this.enableMovement = true;
  // }

  setLinearVelocity( lv ) {
    if( !isNull( this.physicsBody ) ) {
      this.physicsBody.setLinearVelocity( toAmmoType( lv ) );
    }
  }

  getLinearVelocity( ) {

    const v = new THREE.Vector3();

    if( this.physicsBody ) {

      const lv = this.physicsBody.getLinearVelocity();
      v.set( lv.x(), lv.y(), lv.z() );

    }

    return v;

  }

  applyForce( force , position ) {

    position = isNull(position) ? new THREE.Vector3( 0, 0, 0 ) : position;

    if( this.physicsBody ) {

      this.physicsBody.applyForce(
        toAmmoType( force ),
        toAmmoType( position )
      );

    }

  }

  applyImpulse( impulse, position ) {

    if( isNull(position) ) {
      position = new THREE.Vector3( 0, 0, 0 );
    }

    if( this.physicsBody ) {

      this.physicsBody.applyImpulse( toAmmoType( impulse ), toAmmoType( position ) );

    }
  }

  setPosition( position ) {

    this.position.copy( position );
    this.needsUpdate = true;

  }

  setQuaternion( quaternion ) {

    this.quaternion.copy( quaternion );
    this.needsUpdate = true;

  }

  setFriction( friction ) {
    if( this.physicsBody ) {
      this.physicsBody.setFriction( friction );
    }
    this.friction = friction;
  }

  enableGravity( enable=true ) {
    if( this.physicsBody ) {
      const gravity = enable ? settings.gravity.get() : 0;
      this.physicsBody.applyGravity( new AmmoLib.btVector3( 0, gravity, 0 ) );
    }
  }

  enalbeAxes( enable=true, size=1 ) {

    if( enable && isNull( this.axes ) ) {

      this.axes = addAxesHelper( this.threeObject, size );  // for test

    }

    if( !isNull( this.axes ) ) {
      this.axes.visible = enable;
    }

  }

  enableWireframe( enable=true ) {

    if( this.threeObject && this.threeObject.material ) {
      this.threeObject.material.wireframe = enable;
    }

  }

  enableShadow( enable=true ) {

    if ( this.threeObject ) {
      this.threeObject.receiveShadow = enable;
      this.threeObject.castShadow = enable;
    }

  }

  updateWorldTransform( position, quaternion ) {

    const worldTransform = this.worldTransform;
    
    if ( !isNull( this.threeObject ) ) {

      this.threeObject.position.copy( position );
      this.threeObject.quaternion.copy( quaternion );

    }

    if( !isNull( this.physicsBody ) ) {

      const ms = this.physicsBody.getMotionState();
      ms.getWorldTransform( worldTransform );
      worldTransform.setOrigin( toAmmoType( position ) );
      worldTransform.setRotation( toAmmoType( quaternion ) );
      this.physicsBody.setWorldTransform( worldTransform );
      
    }

  }

  update() {

    const worldTransform = this.worldTransform;

    if( this.needsUpdate ) {
 
      this.updateWorldTransform( this.position, this.quaternion );
      this.needsUpdate = false;
      return;

    }

    if( isNull( this.threeObject) || isNull( this.physicsBody ) ) return;

    const ms = this.physicsBody.getMotionState();

    if( this.linearMotor ){
      this.physicsBody.setLinearVelocity( toAmmoType( this.targetLinearVelocity ) );
    }

    if( this.angularMotor ){
      this.physicsBody.setAngularVelocity( toAmmoType( this.targetAngularVelocity ) );
    }

    if ( ms ) {

      ms.getWorldTransform( worldTransform );
      const p = worldTransform.getOrigin();
      const q = worldTransform.getRotation();

      this.threeObject.position.set( p.x(), p.y(), p.z() );
      this.threeObject.quaternion.set( q.x(), q.y(), q.z(), q.w() );

      this.position.copy( this.threeObject.position );
      this.quaternion.copy( this.threeObject.quaternion );

    }

  }

  // 默认释放资源函数：仅支持刚体
  dispose() {

    if( this.scene && this.threeObject ) {
      this.scene.remove( this.threeObject );
    }
    
    if( this.world && this.physicsBody ) {
      this.world.removeRigidBody( this.physicsBody );
    }

    disposeMesh( this.threeObject );
    
    this.threeObject = null;
    this.physicsBody = null;
    
  }

}

export default A3Actor;